#
# peerscape.db.util
#
# Copyright 2006-2008 Helsinki Institute for Information Technology
# and the authors.
#
# Authors: Ken Rimey <rimey@hiit.fi>
#

# Permission is hereby granted, free of charge, to any person
# obtaining a copy of this software and associated documentation files
# (the "Software"), to deal in the Software without restriction,
# including without limitation the rights to use, copy, modify, merge,
# publish, distribute, sublicense, and/or sell copies of the Software,
# and to permit persons to whom the Software is furnished to do so,
# subject to the following conditions:
#
# The above copyright notice and this permission notice shall be
# included in all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
# IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
# CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
# TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
# SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

"""
Utilies for db.py

Having a separate module for this stuff lets you import it without
importing db.py.  This can be helpful in avoiding circular imports.
"""

from __future__ import with_statement

import time
from datetime import datetime

from peerscape.lib import json

def get_timestamp(include_microseconds=False):
    """
    Return a UTC ISO timestamp of the form "yyyy-mm-ddThh:mm:ss".

    >>> len(get_timestamp()) == len('yyyy-mm-ddThh:mm:ss')
    True
    >>> len(get_timestamp(True)) == len('yyyy-mm-ddThh:mm:ss.uuuuuu')
    True
    """
    if include_microseconds:
        return datetime.utcnow().isoformat()
    else:
        return datetime.utcnow().replace(microsecond=0).isoformat()

def parse_json_object(s):
    """
    Decode a JSON object.

    An empty dictionary is returned if the argument string is invalid
    JSON or does not encode a dictionary.

    >>> parse_json_object('{"answer":42}')
    {u'answer': 42}
    >>> parse_json_object('blah')
    {}
    >>> parse_json_object('42')
    {}
    >>> parse_json_object('')
    {}
    """
    try:
        v = json.loads(s)
        return v if isinstance(v, dict) else {}
    except ValueError:
        return {}

def poll(f, on_first_failure=None,
         block=True, timeout=None, polling_interval=0.5):
    """
    Repeatedly invoke a callable until it returns something other than None.
    """
    if timeout is not None:
        assert timeout > 0
        deadline = time.time() + timeout

    while True:
        result = f()
        if on_first_failure and result is None:
            on_first_failure()
            on_first_failure = None

        if result is not None or not block:
            return result

        if timeout is not None:
            remaining = deadline - time.time()
            if remaining <= 0:
                return None
            polling_interval = min(remaining, polling_interval)

        time.sleep(polling_interval)

def sync(src, dst, iid=None):
    """
    Copy data from one database to another.  For testing.
    """
    with dst.write_transaction():
        with src.read_transaction():
            iids = [iid] if iid else src.list_datasets()
            for iid in iids:
                dst.subscribe(iid)
                for num, hash, depends in src.gen_sync_agenda(iid):
                    item = src.get_item_by_hash(hash)
                    dst.put_item(item)
                    if depends:
                        sync(src, dst, depends)

if __name__ == '__main__':
    import doctest
    doctest.testmod()
